<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2021/2/8
 * Time: 10:12
 */
declare(strict_types=1);

namespace App\Service;
use App\Model\Dao\MchAccountAccessDao;
use App\Model\Dao\MchAccountAccessDetailDao;
use App\Model\Dao\MchAccountDao;
use App\Model\Dao\RateSettleDao;
use App\Model\Dao\TradeOrderDetailDao;
use App\Utils\CustomUtils;
use App\Utils\RedisUtils;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use Exception;

/**
 * Class SettleTradeOrderThawBondService
 * @package App\Service
 * 保证金解冻结算
 */
class SettleTradeOrderThawBondService extends CommonService
{

    /**
     * @Inject()
     * @var TradeOrderDetailDao
     */
    private $TradeOrderDetailDao;

    /**
     * @Inject()
     * @var MchAccountDao
     */
    private $MchAccountDao;

    /**
     * @Inject()
     * @var MchAccountAccessDao
     */
    private $MchAccountAccessDao;

    /**
     * @Inject()
     * @var MchAccountAccessDetailDao
     */
    private $MchAccountAccessDetailDao;

    /**
     * @Inject()
     * @var RateSettleDao
     */
    private $RateSettleDao;

    public function statAndPush () {
        $ids = $this->TradeOrderDetailDao->getNeedThawBondIds();
        if (count($ids) > 0) {
            foreach ($ids as $value) {
                $data = [
                    "accessType"    => ACCESS_TYPE_BOND_THAW,
                    "orderId"       => $value["id"],
                    "mchAccountId"  => $value["mchAccountId"]
                ];
                RedisUtils::redisResource()->rPush("MchAccountAccessList", serialize($data));
            }
        }
    }

    public function handle($param) {
        // 校验
        $tradeOrder = $this->TradeOrderDetailDao->get($param['orderId']);
        if(!$tradeOrder) {
            throw new Exception('订单不存在，终止处理', FAILED_CODE);
        }
        if($tradeOrder['isSettle'] != 2) {
            throw new Exception('订单未结算', FAILED_CODE);
        }
        if($tradeOrder['bondThawSettleStatus'] == 1) {
            throw new Exception('订单已经保证金解冻结算', FAILED_CODE);
        }
        $isAccess = $this->MchAccountAccessDao->getWhere(['orderId' => $param['orderId'], 'accessType' => ACCESS_TYPE_BOND_THAW]);
        if ($isAccess) {
            throw new Exception('订单已经进行保证金解冻结算', FAILED_CODE);
        }
        $mchAccount = $this->MchAccountDao->get($param['mchAccountId']);
        if(!$mchAccount) {
            throw new Exception('查询账户记录失败', FAILED_CODE);
        }

        // 乐观锁版本号+1
        $updateVersion = $mchAccount['updateVersion'] + 1;

        // 保证金可提现余额 余额前后
        $bondAmount = $tradeOrder['bondAmount'];
        $beforeTradeCashAmount = $mchAccount['tradeCashAmount'];
        $afterTradeCashAmount = $mchAccount['tradeCashAmount'] + $bondAmount;

        // 保证金冻结余额 余额前后
        $beforeBondFrozenAmount = $mchAccount['bondFrozenAmount'];
        $afterBondFrozenAmount = $mchAccount['bondFrozenAmount'] - $bondAmount;

        // 总的解冻保证金金额
        $bondThawAmount = $mchAccount['bondThawAmount'] + $bondAmount;

        // 开启事务
        Db::beginTransaction();
        try {

            // step
            // 1.保证金冻结账户出账
            // 2.交易可提现账户入账
            // 3.更新订单状态
            // 4.更新账户余额

            // step 1
            $this->handleOutBondAmount($param, $tradeOrder, $mchAccount, $bondAmount, $beforeBondFrozenAmount, $afterBondFrozenAmount);

            // step 2
            $this->handleInTradeCashAmount($param, $tradeOrder, $mchAccount, $bondAmount, $beforeTradeCashAmount, $afterTradeCashAmount);

            // step 3
            $updateTradeOrder = array(
                'bondThawSettleStatus'  => 1,
                'bondThawSettleTime'    => time()
            );
            $this->TradeOrderDetailDao->update($param['orderId'], $updateTradeOrder);

            // step 4
            $updateMchAccount = array(
                "tradeCashAmount"   => $afterTradeCashAmount,
                "bondFrozenAmount"  => $afterBondFrozenAmount,
                "bondThawAmount"    => $bondThawAmount,
                "updateVersion"     => $updateVersion
            );
            $result = $this->MchAccountDao->updateWhere([
                "id" => $param['mchAccountId'],
                "updateVersion" => $mchAccount['updateVersion']
            ], $updateMchAccount);

            // 如果更新账户失败，重试，最多3次
            if (!$result) {
                throw new \Exception("更新账户失败");
            }

        } catch (\Throwable $e) {
            CustomUtils::xzxDebug($e->getMessage());
            Db::rollBack();

            // 失败重试，最多3次
            $this->tryOneMore($param);

            return CustomUtils::xeServiceReturnData(FAILED_CODE, "交易订单保证金解冻结算失败");
        }
        Db::commit();

        return CustomUtils::xeServiceReturnData(SUCCESS_CODE, "交易订单保证金解冻结算成功");
    }

    /**
     * @param $param
     * @param $tradeOrder
     * @param $mchAccount
     * @param $amount
     * @param $beforeAmount
     * @param $afterAmount
     * 保证金冻结账户保证金出账
     */
    private function handleOutBondAmount($param, $tradeOrder, $mchAccount, $amount, $beforeAmount, $afterAmount) {

        // 写主表
        $mchAccountAccess = array(
            "id"           => CustomUtils::createUUID(),
            "mchId"        => $tradeOrder["mchId"],
            "mchNo"        => $tradeOrder["mchNo"],
            "mchAccountId" => $param['mchAccountId'],
            "accessType"   => ACCESS_TYPE_BOND_THAW,
            "accountType"  => ACCOUNT_TYPE_BOND_FROZEN,
            "orderId"      => $tradeOrder['id'],
            "tradeNo"      => $tradeOrder['tradeNo'],
            "amount"       => -$amount,
            "currency"     => $tradeOrder['settleCurrency'],
            "status"       => 1,
            "remark"       => "保证金冻结账户解冻出账",
            "createTime"   => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDao->add($mchAccountAccess);

        // 写明细表
        $mchAccountAccessDetail = array(
            "id"                => CustomUtils::createUUID(),
            "mchId"             => $tradeOrder["mchId"],
            "mchNo"             => $tradeOrder["mchNo"],
            "mchAccountId"      => $param['mchAccountId'],
            "accessId"          => $mchAccountAccess['id'],
            "accountType"       => ACCOUNT_TYPE_BOND_FROZEN,
            "inOutType"         => DETAIL_TYPE_OUT,
            "type"              => TYPE_BOND_THAW_FROZEN_OUT,
            "beforeTotalAmount" => $beforeAmount,
            "afterTotalAmount"  => $afterAmount,
            "amount"            => -$amount,
            "orderId"           => $tradeOrder['id'],
            "tradeNo"           => $tradeOrder['tradeNo'],
            "currency"          => $tradeOrder['settleCurrency'],
            "remark"            => "保证金冻结账户解冻出账",
            "createTime"        => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDetailDao->add($mchAccountAccessDetail);
    }

    /**
     * @param $param
     * @param $tradeOrder
     * @param $mchAccount
     * @param $amount
     * @param $beforeAmount
     * @param $afterAmount
     * 交易可提现账户保证金入账
     */
    private function handleInTradeCashAmount($param, $tradeOrder, $mchAccount, $amount, $beforeAmount, $afterAmount) {

        // 写主表
        $mchAccountAccess = array(
            "id"           => CustomUtils::createUUID(),
            "mchId"        => $tradeOrder["mchId"],
            "mchNo"        => $tradeOrder["mchNo"],
            "mchAccountId" => $param['mchAccountId'],
            "accessType"   => ACCESS_TYPE_BOND_THAW,
            "accountType"  => ACCOUNT_TYPE_CASH,
            "orderId"      => $tradeOrder['id'],
            "tradeNo"      => $tradeOrder['tradeNo'],
            "amount"       => $amount,
            "currency"     => $tradeOrder['settleCurrency'],
            "status"       => 1,
            "remark"       => "保证金交易可提现账户入账",
            "createTime"   => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDao->add($mchAccountAccess);

        // 写明细表
        $mchAccountAccessDetail = array(
            "id"                => CustomUtils::createUUID(),
            "mchId"             => $tradeOrder["mchId"],
            "mchNo"             => $tradeOrder["mchNo"],
            "mchAccountId"      => $param['mchAccountId'],
            "accessId"          => $mchAccountAccess['id'],
            "accountType"       => ACCOUNT_TYPE_CASH,
            "inOutType"         => DETAIL_TYPE_IN,
            "type"              => TYPE_BOND_THAW_CASH_IN,
            "beforeTotalAmount" => $beforeAmount,
            "afterTotalAmount"  => $afterAmount,
            "amount"            => $amount,
            "orderId"           => $tradeOrder['id'],
            "tradeNo"           => $tradeOrder['tradeNo'],
            "currency"          => $tradeOrder['settleCurrency'],
            "remark"            => "保证金交易可提现账户入账",
            "createTime"        => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDetailDao->add($mchAccountAccessDetail);
    }

}